<?php

declare(strict_types=1);

/*
 * This file is part of PHP CS Fixer.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace PhpCsFixer\Tests\Fixer\FunctionNotation;

use PhpCsFixer\Tests\Test\AbstractFixerTestCase;

/**
 * @internal
 *
 * @covers \PhpCsFixer\Fixer\FunctionNotation\MultilinePromotedPropertiesFixer
 *
 * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\FunctionNotation\MultilinePromotedPropertiesFixer>
 *
 * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\FunctionNotation\MultilinePromotedPropertiesFixer
 *
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
 */
final class MultilinePromotedPropertiesFixerTest extends AbstractFixerTestCase
{
    /**
     * @param _AutogeneratedInputConfiguration $configuration
     *
     * @requires PHP 8.0
     *
     * @dataProvider provideFixCases
     */
    public function testFix(string $expected, ?string $input = null, array $configuration = []): void
    {
        $this->fixer->configure($configuration);
        $this->doTest($expected, $input);
    }

    /**
     * @return iterable<string, array{0: string, 1: null|string, 2?: _AutogeneratedInputConfiguration}>
     */
    public static function provideFixCases(): iterable
    {
        yield 'single parameter' => [
            '<?php class Foo {
                public function __construct(
                    public int $x
                ) {}
            }',
            '<?php class Foo {
                public function __construct(public int $x) {}
            }',
        ];

        yield 'single parameter with trailing comma' => [
            '<?php class Foo {
                public function __construct(
                    protected int $x,
                ) {}
            }',
            '<?php class Foo {
                public function __construct(protected int $x,) {}
            }',
        ];

        yield 'multiple parameters' => [
            '<?php class Foo {
                public function __construct(
                    private int $x,
                    private int $y,
                    private int $z
                ) {}
            }',
            '<?php class Foo {
                public function __construct(private int $x, private int $y, private int $z) {}
            }',
        ];

        yield 'multiple parameters and only one promoted' => [
            '<?php class Foo {
                public function __construct(
                    int $x,
                    private int $y,
                    int $z
                ) {}
            }',
            '<?php class Foo {
                public function __construct(int $x, private int $y, int $z) {}
            }',
        ];

        yield 'parameters with default values' => [
            '<?php class Foo {
                public function __construct(
                    private array $a = [1, 2, 3, 4],
                    private bool $b = self::DEFAULT_B,
                ) {}
            }',
            '<?php class Foo {
                public function __construct(private array $a = [1, 2, 3, 4], private bool $b = self::DEFAULT_B,) {}
            }',
        ];

        yield 'parameters with attributes' => [
            '<?php class Foo {
                public function __construct(
                    private array $a = [1, 2, 3, 4],
                    #[Bar(1, 2, 3)] private bool $b = self::DEFAULT_B,
                ) {}
            }',
            '<?php class Foo {
                public function __construct(private array $a = [1, 2, 3, 4], #[Bar(1, 2, 3)] private bool $b = self::DEFAULT_B,) {}
            }',
        ];

        yield 'multiple classes' => [
            '<?php
            class ClassWithSinglePromotedProperty {
                public function __construct(
                    private int $foo
                ) {}
            }
            class ClassWithoutConstructor {}
            class ClassWithoutPromotedProperties {
                public function __construct(string $a, string $b) {}
            }
            class ClassWithMultiplePromotedProperties {
                public function __construct(
                    private int $x,
                    private int $y,
                    private int $z
                ) {}
            }',
            '<?php
            class ClassWithSinglePromotedProperty {
                public function __construct(private int $foo) {}
            }
            class ClassWithoutConstructor {}
            class ClassWithoutPromotedProperties {
                public function __construct(string $a, string $b) {}
            }
            class ClassWithMultiplePromotedProperties {
                public function __construct(private int $x, private int $y, private int $z) {}
            }',
        ];

        yield '0 parameters with 0 configured' => [
            '<?php class Foo {
                    public function __construct() {}
                }',
            null,
            ['minimum_number_of_parameters' => 0],
        ];

        foreach ([0, 1, 2] as $numberOfParameters) {
            yield \sprintf('2 parameters with %d configured', $numberOfParameters) => [
                '<?php class Foo {
                    public function __construct(
                        private int $x,
                        private int $y
                    ) {}
                }',
                '<?php class Foo {
                    public function __construct(private int $x, private int $y) {}
                }',
                ['minimum_number_of_parameters' => $numberOfParameters],
            ];

            yield \sprintf('2 parameters and only one promoted with %d configured', $numberOfParameters) => [
                '<?php class Foo {
                    public function __construct(
                        int $x,
                        private int $y
                    ) {}
                }',
                '<?php class Foo {
                    public function __construct(int $x, private int $y) {}
                }',
                ['minimum_number_of_parameters' => $numberOfParameters],
            ];
        }

        foreach ([3, 4] as $numberOfParameters) {
            yield \sprintf('2 parameters with %d configured', $numberOfParameters) => [
                '<?php class Foo {
                    public function __construct(private int $x, private int $y) {}
                }',
                null,
                ['minimum_number_of_parameters' => $numberOfParameters],
            ];

            yield \sprintf('2 parameters and only one promoted with %d configured', $numberOfParameters) => [
                '<?php class Foo {
                    public function __construct(int $x, private int $y) {}
                }',
                null,
                ['minimum_number_of_parameters' => $numberOfParameters],
            ];
        }

        foreach ([1, 2, 3, 4] as $numberOfParameters) {
            yield \sprintf('2 parameters and none promoted with %d configured', $numberOfParameters) => [
                '<?php class Foo {
                    public function __construct(int $x, int $y) {}
                }',
                null,
                ['minimum_number_of_parameters' => $numberOfParameters],
            ];
        }

        yield 'blank lines removed' => [
            '<?php class Foo {
                public function __construct(
                    private int $x,
                    private int $y,
                    private int $z
                ) {}
            }',
            '<?php class Foo {
                public function __construct(
                    private int $x,

                    private int $y,

                    private int $z
                ) {}
            }',
        ];

        yield 'no blank lines' => [
            '<?php class Foo {
                public function __construct(
                    private int $x,
                    private int $y,
                    private int $z
                ) {}
            }',
            '<?php class Foo {
                public function __construct(
                    private int $x, private int $y,

                    private int $z
                ) {}
            }',
        ];

        yield 'blank lines kept' => [
            '<?php class Foo {
                public function __construct(
                    private int $x,
                    private int $y,

                    private int $z
                ) {}
            }',
            '<?php class Foo {
                public function __construct(
                    private int $x, private int $y,

                    private int $z
                ) {}
            }',
            ['keep_blank_lines' => true],
        ];
    }
}
